#ifndef SMART_OBJECT_H
#define SMART_OBJECT_H

#include <map>
#include <set>
#include <string>

#include <iostream>

// Added by Steve
struct Stimulus;
struct Receptor;
float GenericStimRecOperation( const Receptor &Rec, const Stimulus &Stim, float maxRecVal );
float GenericRecOperation( const Receptor &Rec);
float GenericStimOperation( const Stimulus &Stim);

struct Stimulus {
  typedef unsigned int ID;
  typedef float (*StimFunc)( const Stimulus& stim );

  Stimulus();
  bool operator()( const Stimulus& a, const Stimulus& b );

  ID attributeID;
  float weight;

  StimFunc stimulusEvaluation;
};

struct Receptor {
  typedef unsigned int ID;
  typedef float (*RecepFunc)( const Receptor& rep );
  typedef float (*RecepStimFunc)( const Receptor& rep, const Stimulus& stim, float maxRecVal );

  Receptor();
  bool operator()( const Receptor& a, const Receptor& b );

  ID attributeID;
  float currentState;

  RecepFunc     receptorEvaluation;
  RecepStimFunc pairEvaluation;
};

class SmartObject {
  public:
    typedef std::set< Stimulus, Stimulus > StimSet;
    typedef StimSet::const_iterator StimIter;
    
    SmartObject();
    SmartObject( std::string name );

    void AddStimulus( std::string name, float weight, 
                      Stimulus::StimFunc stimFunc = 0 );
    void AddStimulus( Stimulus::ID id, float weight, 
                      Stimulus::StimFunc stimFunc = 0 );
    StimIter ChangeStimulus( std::string name, float weight, 
                             Stimulus::StimFunc stimFunc = 0 );
    StimIter ChangeStimulus( Stimulus::ID id, float weight, 
                             Stimulus::StimFunc stimFunc = 0 );
    void RemoveStimulus( std::string name );
    void RemoveStimulus( Stimulus::ID name );

    void SetName( std::string name );
    const std::string& GetName();
    const StimSet& GetStimulants();
  private:
    std::string name;
    StimSet stimulants;
};

class SmartAgent {
  public:
    typedef std::set< Receptor, Receptor > RecepSet;
    typedef RecepSet::const_iterator RecepIter;
    
    SmartAgent();
    SmartAgent( std::string name );

    void AddReceptor( std::string name, float initState,
                      Receptor::RecepStimFunc pairFunc = 0,
                      Receptor::RecepFunc recepFunc = 0 );
    void AddReceptor( Receptor::ID id, float initState,
                      Receptor::RecepStimFunc pairFunc = 0,
                      Receptor::RecepFunc recepFunc = 0 );
    RecepIter ChangeReceptor( std::string name, float initState,
                              Receptor::RecepStimFunc pairFunc = 0,
                              Receptor::RecepFunc recepFunc = 0 );
    RecepIter ChangeReceptor( Receptor::ID id, float initState,
                              Receptor::RecepStimFunc pairFunc = 0,
                              Receptor::RecepFunc recepFunc = 0 );
    void RemoveReceptor( std::string name );
    void RemoveReceptor( Receptor::ID id );

    float EvaluateObject( SmartObject& smartObject, int& pairCount );
    
    void UpdateWithObject( SmartObject& smartObject );

    void SetName( std::string name );
    const std::string& GetName();
    const RecepSet& GetReceptors();

    float GetMaxReceptor() const;
    void SmartAgent::FindNewMaxReceptor();
  private:
    std::string name;
    RecepSet receptors;
    float maxReceptor;
};

class AttributeIdGenerator {
  public:
    typedef unsigned int ID;
    typedef std::map<std::string, ID> StrIdMap;
    typedef StrIdMap::iterator StrIdIter;
    typedef std::pair<std::string, ID> StrIdPair;
    typedef std::pair<StrIdIter,bool> StrIdInsert;

    AttributeIdGenerator();

    ID GetId( std::string name );
    std::string GetName( ID id );
  private:
    StrIdMap attributeToIdMap;
    ID currentNewId;
};

extern AttributeIdGenerator gSmartObjectIDGenerator;

#endif
